package com.xueqiu.movieplaymanagersystem.utils.sensitiveUtils;

import java.io.Serializable;
import java.util.HashMap;
import java.util.TreeMap;

/**
 * 没有安全检查；如数组越界检查
 * 没有注释的方法与{@link String}中的方法相似
 * <p>
 * 成员变量hash，可以作为{@link HashMap}和{@link TreeMap}的key
 *
 * @author xueqiu
 */
public class StringPointer implements Serializable, CharSequence, Comparable<StringPointer> {
    private static final long serialVersionUID = 1L;

    /**
     * 结构内容
     */
    protected final char[] value;

    /**
     * 偏移量，默认0
     */
    protected final int offset;

    /**
     * 结构长度；等于 最大索引+1
     */
    protected final int length;

    /**
     * 结构哈希标志
     */
    private int hash = 0;

    public StringPointer(String str) {
        value = str.toCharArray();
        offset = 0;
        length = value.length;
    }

    public StringPointer(char[] value, int offset, int length) {
        this.value = value;
        this.offset = offset;
        this.length = length;
    }

    /**
     * hash值生成；从i索引开始，向后数2个字符；考虑偏移量
     * 例子：i = 0, length = 5, 则索引0,索引1这两个字符参与生成
     * <p>
     * 因为是2个char类型的计算
     * 所以返回值一定不会大于0000 0000 0000 0010 0000 0000 0000 0000，
     * 也就是2^17。也就是DEFAULT_INITIAL_CAPACITY
     *
     * @param i 取值范围从 0 到 length - 2
     * @return 简单算法生成的hash值
     */
    public int nextTwoCharHash(int i) {
        //最基本的哈希算法
        return 31 * (int) value[offset + i] + (int) value[offset + i + 1];
    }

    /**
     * mix值生成；从i索引开始，向后数2个字符；考虑偏移量
     * 例子：i = 0, length = 5, 则索引0,索引1这两个字符参与生成
     * 规则：2个字符执行“位运算‘或’得到的值”
     * 用途：粗糙地判断2个敏感词是否相同
     *
     * @param i 取值范围从 0 到 length - 2
     * @return 简单算法生成的mix值
     */
    public int nextTwoCharMix(int i) {
    	/*
    	使用了位移运算符 << 将 value 数组中偏移量为 offset + i 的字符左移 16 位，
    	再使用位或运算符 | 将该字符与偏移量为 offset + i + 1 的字符做组合
    	char是2个字节，16位；int是4个字节，32位
    	例子：
            0000 0000 0000 1111 强转-> 0000 0000 0000 0000 0000 0000 0000 1111 左移-> 0000 0000 0000 1111 0000 0000 0000 0000
            0101 0101 0101 0101 强转-> 0000 0000 0000 0000 0101 0101 0101 0101
            计算
            0000 0000 0000 1111 0000 0000 0000 0000
            |
            0000 0000 0000 0000 0101 0101 0101 0101
            结果为
            0000 0000 0000 1111 0101 0101 0101 0101
    	从“二进制位”角度上保留了两个char每一位上的状态；所以才说“mix值一致，则敏感词相同”
    	 */
        return (((int) value[offset + i]) << 16) | (int) value[offset + i + 1];
    }

    /**
     * 判断，从i索引开始的子字符串，是否以目标词开头
     * 例子：abcdefg,从i=1开始,判断是否以word=bcd开头
     *
     * @param i    取值范围从 0 到 length - 1
     * @param word 词
     * @return true/false
     */
    public boolean nextStartsWith(int i, StringPointer word) {
        //判断字串长度小于目标词长度
        if (word.length > length - i) {
            return false;
        }
        /*
        从目标词的尾索引的字符开始判断；
        offset + i 是当前字符串片段的起始索引
        offset + i + c 是当前要对比的索引
        */
        for (int currentWordIndex = word.length - 1; currentWordIndex >= 0; currentWordIndex--) {
            if (value[offset + i + currentWordIndex] != word.value[word.offset + currentWordIndex]) {
                return false;
            }
        }
        return true;
    }

    /**
     * 填充；从索引begin开始，到索引(end-1)结束的字串，每个字符替换为targetChar
     *
     * @param begin      从此位置开始（含）；值是索引
     * @param end        到此位置结束（不含）；值是索引
     * @param targetChar 以此字符填充（替换）
     */
    public void fill(int begin, int end, char targetChar) {
        for (int i = begin; i < end; i++) {
            value[offset + i] = targetChar;
        }
    }

    @Override
    public int length() {
        return length;
    }

    @Override
    public char charAt(int i) {
        return value[offset + i];
    }

    public StringPointer substring(int begin) {
        return new StringPointer(value, offset + begin, length - begin);
    }

    public StringPointer substring(int begin, int end) {
        return new StringPointer(value, offset + begin, end - begin);
    }

    @Override
    public CharSequence subSequence(int start, int end) {
        return substring(start, end);
    }

    @Override
    public String toString() {
        return new String(value, offset, length);
    }

    @Override
    public int hashCode() {
        int h = hash;
        if (h == 0 && length > 0) {
            for (int i = 0; i < length; i++) {
                h = 31 * h + value[offset + i];
            }
            hash = h;
        }
        return h;
    }

    @Override
    public boolean equals(Object thatObject) {
        if (this == thatObject) {
            return true;
        }
        if (thatObject instanceof StringPointer) {
            StringPointer that = (StringPointer) thatObject;
            if (length == that.length) {
                for (int i = 0; i < this.length; i++) {
                    if (this.value[this.offset + i] != that.value[that.offset + i]) {
                        return false;
                    }
                }
                return true;
            }
        }
        return false;
    }

    @Override
    public int compareTo(StringPointer that) {
        int thisLength = this.length;
        int thatLength = that.length;
        int min = Math.min(thisLength, thatLength);

        int k = 0;
        while (k < min) {
            char c1 = this.value[this.offset + k];
            char c2 = that.value[that.offset + k];
            if (c1 != c2) {
                return c1 - c2;
            }
            k++;
        }
        return thisLength - thatLength;
    }
}
